#include "logic.h"


Logic::Logic(void) {

}

Logic::~Logic(void) {

}

vector <string> Logic::start(string userInput) {
	parseUserInput(userInput);
	executeCommand();
	clearUserTask();
	return _forOutputBox;
}

void Logic::parseUserInput(const string & userInput) {

	string command;
	string taskName;
	string deadline;
	string priority;
	
	string time; // for the calendar
	string temporary;
	char symbol;
	string keyword;
	string syntax;

	string symboldirectory = "@!";
	int taskLine;

	istringstream inStream(userInput);

	inStream>> command;

	if (command == ADD){
		inStream>> taskName;
		while (inStream >>symbol){
			if(symbol == symboldirectory[0]){
				inStream >> deadline >> time;
			}else if(symbol == symboldirectory[1]){
				inStream >> priority;
				}
		}

	} else if (command == DISPLAY){
		inStream >> taskName;
		if(taskName == EMPTY){
			taskName = DEFAULT; // default display by deadline-> priority
			}
	} else if (command == MODIFY){
			inStream >> taskLine >> taskName; // @ or ! or taskname goes into taskName to be extracted later
			if(taskName == "@" || taskName == "!"){
				if(taskName == "@"){
					inStream >> deadline;
				} else {
					inStream >> priority;
				}
			} 
	} else if (command == ARCHIVE){
				inStream >> taskLine;
				getline(inStream, temporary);
				if(temporary!= CANNOT_MAKE_IT)
					temporary = COMPLETED;
	} else if (command == DELETE){
			inStream >> taskLine;
	} else if (command == SEARCH){
			inStream>> taskName >> temporary;  // the @ and ! not stored into teh Task, instread check which, deadline or priority, is indicated
			if(temporary != EMPTY){
				if(temporary == "@"){
					inStream >> deadline;
				} else {
					inStream >> priority;
				}
			} 
	} else if (command == SET){
			inStream >> taskLine >> deadline; // stores number of days in deadline so check command before accessing deadline when setting 
	} else if (command == HELP || command == FAST_HELP){
			inStream >> taskName; // if taskName is mpty then show all
	}

		_userTask.changeCommand(command);
		_userTask.changeTaskName(taskName);
		_userTask.changeDeadline(deadline);
		_userTask.changePriority(priority);
		_userTask.changeTime(time);
	return;
}

void Logic::executeCommand() {

	// depending _command in _userTask,
	// execute different functions accordingly

	// please do executeCommad() in class storage
	// as well, it should be similar, if not identical
}

void Logic::addTask() {
	//
	// class logic knows that something is to be added, but only storage can do it
	// so it throws the job to storage.
	// create the add function in storage class	
	//

	_forOutputBox = _store.start(_userTask);
	return;
}

 
void Logic::deleteTask() {
 
        //
 
        // class logic knows that something is to be deleted, but only storage can do it
 
        // so it throws the job to storage.
 
        // create the delete function in storage class  
 
        //
 
        // now delete by matching string, not matching line number
 
        // cause the numbering in the list displayed, may not be the same
 
        // number as in our storage
 
        if(_userTask.getTaskName() == EMPTY) {
 
                _userTask.changeTaskName(_list[_userTask.getTaskLine()-1].getTaskName());
 
        }
 
        _forOutputBox = _store.start(_userTask);
 
        return;
 
}
 

 
void Logic::display() {
        _list = _store.getStorage();

        // displayList now contains all the various tasks
        // take each task, make into string and put the numbering
        // to be displayed.
        //
        // shove everything to be displayed into _forOutputBox
        // and return;

	    // CAUTION _list IS A VECTOR <TASK>
        // WHILE _forOutputBox IS A VECTOR <STRING>
 
}
 

 
void Logic::modify() {
 
        //
 
        // class logic knows that something is to be modify, but only storage can do it
 
        // so it throws the job to storage.
 
        // create the modify function in storage class  
 
        //
 
        if(_userTask.getTaskName() == EMPTY) {
 
                _userTask.changeTaskName(_list[_userTask.getTaskLine()-1].getTaskName());
 
        }
 
        _forOutputBox = _store.start(_userTask);
 
        return;
 
}
 

 
void Logic::reset() {
 
        //
 
        // class logic knows that schedule is to be reset, but only storage can do it
 
        // so it throws the job to storage.
 
        // create the reset function in storage class   
 
        //
 
        
 
        _forOutputBox = _store.start(_userTask);
 
        return;
 
}
 

 
void Logic::search() {
        _list = _store.getStorage();
        vector<Task> forOutputBox;
        string partialTaskName;
 
        for(vector<Task>::iterator iter = _list.begin(); iter != _list.end(); iter++) {
                int i = 0;
                for(string::iterator iterS = ((*iter).getTaskName()).begin(); iterS != ((*iter).getTaskName()).end(); iterS++) {
 
                        partialTaskName = ((*iter).getTaskName()).substr(i,(_userTask.getTaskName()).size());

                        if(calculateLevenshteinDistance(_userTask.getTaskName(),partialTaskName) < 3) {
                                forOutputBox.push_back(*iter);
                                break;
                        }
                        i++;
                }
        }
// convert vector<Task> forOutputBox -> vector<string> _forOutputBox to print in GUI
        return;
}
 

 

 

 
// this function calculates the number of moves required
 
// to transform string 1 into string 2
 
// (e.g. string1 = abcde, string2 = abdcx)
 
// c -> d, d -> c, e -> x
 
// 3 moves, thus the function will return 3
 
int Logic::calculateLevenshteinDistance(const string & string1, const string & string2) { 
        int m(string1.size());  
        int n(string2.size());

        if(m == 0) {
                return n;
        }
 
        if(n == 0) {
                return m;
        }
        int * typos = new int[n+1];   
 
        for(int i = 0; i < n+1; i++ ) 
                typos[i] = i;   
 
        int i = 0;  
 
        for(int iter1 = 0; iter1 < m; iter1++, i++) {
                typos[0] = i+1;    
				int corner = i;    
                int j = 0;    
 
                for(int iter2 = 0; iter2 < n; iter2++, j++ ) {
                        int upper = typos[j+1];
 
                        if(string1[iter1] == string2[iter2]) {
                                typos[j+1] = corner;     
                        } else {
                                int temp(upper<corner ? upper:corner); 
                                typos[j+1] = (typos[j]<temp ? typos[j]:temp)+1;  
                        }      
 
                        corner = upper;  
                } 
        }  
 
        int levenshteinDistance = typos[n];  
        delete [] typos; 
        
		return levenshteinDistance;
}
 

 
void Logic::reminder() {
 
        time_t rawtime;
        time(&rawtime);  /* current time in seconds */
 
        struct tm * currentTime;
 
        currentTime = localtime(&rawtime);
 
        (*currentTime).tm_hour = 0;                             // making current time point to today at 00:00
        (*currentTime).tm_min = 0;
        (*currentTime).tm_sec = 0;
 

 
        time_t today = mktime(currentTime);
 
        _list = _store.getStorage();
        vector<Task> toBeReminded;
 
        for(vector<Task>::iterator iter = _list.begin(); iter != _list.end(); iter++) {
 
                istringstream read((*iter).getDeadline());
 
                int day;
                int month;
                char dummy;
 
                read >> day;
                read >> dummy;
                read >> month;
                month -= 1;
 
                struct tm * dl;
 
                dl = localtime(&rawtime);
                (*dl).tm_hour = 0; (*dl).tm_min = 0; (*dl).tm_sec = 0;
                (*dl).tm_mon = month;  (*dl).tm_mday = day;
                time_t deadline = mktime(dl);

                if(difftime(deadline,today) <= 60*60*24*3 && (*iter).getPriority() == "high") {
                        toBeReminded.push_back(*iter);
                } else if(difftime(deadline,today) <= 60*60*24*2 && (*iter).getPriority() == "medium") {
                        toBeReminded.push_back(*iter);
                } else if(difftime(deadline,today) <= 60*60*24*1 && (*iter).getPriority() == "low") {
                        toBeReminded.push_back(*iter);
 
                }
 
        }
 

 
// transfer vector<Task> toBeReminded -> vector<string> _forOutputBox;
 

 
        return;
 
}
 

 
void Logic::undo() {
 
        //
 
        // class logic knows that it has to undo something, but only storage can do it
 
        // so it throws the job to storage.
 
        // create the undo function in storage class    
 
        //
 
        
 
}
 

 
void Logic::help() {

        // list all available options if userInput is "help"
 
        //
 
        // describe how to use the add function if userInput is "help add"
 
        // must describe ALL FUNCTIONS AVAILABLE
 
        //
 
        // u will need to create many more functions like helpAdd(), helpDelete() bla bla bla
 
        //
 

 
}
 

 
void Logic::archive() {
 
        //
 
        // class logic knows that it has to archive something, but only storage can do it
 
        // so it throws the job to storage.
 
        // create the undo function in storage class    
 
        //
        _forOutputBox = _store.start(_userTask);
 
        return;
 
} 


void Logic::clearUserTask() {
	_userTask.changeCommand = "";
	_userTask.changeDeadline = "";
	_userTask.changePriority = "";
	_userTask.changeTaskLine = "";
	_userTask.changeTaskName = "";
	_userTask.changeTime= "";
	return;
}